/*
 * FNISVGEditor - JavaScript - based editor for SVG graphics 
 * Copyright (C) 2008 - 2009 Antti Leppä / Foyt
 * http://www.foyt.fi
 * 
 * License: 
 * 
 * Licensed under GNU Lesser General Public License Version 2.1 or later (the "LGPL") 
 * http://www.gnu.org/licenses/lgpl.html
 */
FNISelectionManager = Class.create({
  initialize: function(editorInstance){
    this._editorInstance = editorInstance;
    this._selectedElements = new Array();
    this._rectangleController = getSVGElementControllerVault().getElementController('rect');
    this._selectionDimensions = null;
    this._currentIconsetConf = null;
    this._iconsets = null;
    this._selectionBox = null;
    this._observeIcons = false;
    this._iconWidth = 0;
    this._iconHeight = 0;
    this._activeCorner = null;  
          
    this._documentMouseUpListener = this._onDocumentMouseUp.bindAsEventListener(this);    
    this._editorInstance.addListener("mousedragstart", this, this._onEditorMouseDragStart);
    this._editorInstance.addListener("mousedragend", this, this._onEditorMouseDragEnd);
    this._editorInstance.addListener("mousedragging", this, this._onEditorMouseDragging);
    this._editorInstance.addListener("mousedown", this, this._onEditorMouseDown);    
    this._editorInstance.addListener("elementClick", this, this._onEditorMouseElementClick);
    this._editorInstance.addListener("mousemove", this, this._onEditorMouseMove);
    
    Event.observe(document, "mouseup", this._documentMouseUpListener);
    
    this.setMode(FNISelectionManager.MODE_SELECT);
    
    this._editorInstance.getEditorView().addListener("mouseenter", this, this._onMouseEnterEditorView);
    this._editorInstance.getEditorView().addListener("mouseexit", this, this._onMouseExitEditorView);
  },
  /**
   * Sets iconset.
   *
   * @param {Object} iconsetName
   */
  setIconset: function(iconsetName){
    if ((this._currentIconsetConf == null) || (this._currentIconsetConf.iconsetName != iconsetName)) {
      
      if (this._currentIconsetConf != null) {
        var iconset = this._currentIconsetConf.iconset;
        for (var i = 0; i < iconset.length; i++) {
          var icon = iconset[i];
          this._editorInstance.getSVGDocument().removeTemporaryElement(icon);
          this._observeIcons = false;
        }
      }
      
      if (iconsetName != 'none') {
        var iconsetConf = this._loadIconset(iconsetName);
        
        if (iconsetConf != undefined) {
          var svgDocument = this._editorInstance.getSVGDocument();
          this._currentIconsetConf = iconsetConf;
          
          var iconset = this._currentIconsetConf.iconset;
          for (var i = 0; i < iconset.length; i++) {
            var icon = iconset[i];
            this._observeIcons = true;
            FNISVGEditorElementStylingUtils.setStyles(icon, this._currentIconsetConf.style);
            svgDocument.appendTemporaryElement(icon);
          }
          
          this._iconWidth = this._currentIconsetConf.width;
          this._iconHeight = this._currentIconsetConf.height;    
        }
        
        this._updateIconset(this.getSelectionDimensions());
      }
      else {
        this._currentIconsetConf = null;
      }
    }
  },
  setMode: function(mode){
    if (this._mode != mode) {
      this._previousMode = this._mode;
      this._mode = mode;
      
      switch (this._previousMode) {
        case FNISelectionManager.MODE_DRAGGING_ICON:
          var mouseInfo = this._editorInstance.getMouseInfo();
          this.fire("iconDragEnd", {
            mouseEditorX: mouseInfo.x,
            mouseEditorY: mouseInfo.y,
            dragStartX: mouseInfo.dragStartX,
            dragStartY: mouseInfo.dragStartY,
            corner: this._activeCorner
          });
        break;
      }
      
      this.fire("selectionModeChanged", {
        previousMode: this._previousMode,
        mode: this._mode
      });
    }
  },
  setPreviousMode: function(){
    this.setMode(this._previousMode);
  },
  resetMode: function () {
    this._hideSelectionBox();
    this.setMode(FNISelectionManager.MODE_SELECT);
  },
  getMode: function(){
    return this._mode;
  },
  selectElement: function(element){
    if (this.getMode() == FNISelectionManager.MODE_SELECT) {
      
      var selectableElement = element;
      var elementGroup;
      while ((elementGroup = this._getElementGroup(selectableElement)) != null) {
        selectableElement = elementGroup;
      }
      
      if ((this.isSelected(selectableElement) == false) && (this._elementInLayer(selectableElement, this._editorInstance.getSVGDocument().getCurrentLayer()))) {
        this._selectionDimensions = null;
        this.getSelectedElements().push(selectableElement);
        this.renderSelections();
        this.fire("selectionChanged", {});
      }
    }
  },
  deselectElement: function(element){
    var elements = this.getSelectedElements();
    if (elements != null) {
      for (var i = 0; i < elements.length; i++) {
        if (elements[i] == element) {
          this._selectionDimensions = null;
          elements.splice(i, 1);
          this._editorInstance.getSVGDocument().removeTemporaryElement(element._selectionRect);
          element._selectionRect = null;
          this.renderSelections();
          this.fire("selectionChanged", {});
          break;
        }
      }
    }
  },
  isSelected: function(element){
    var elements = this.getSelectedElements();
    if (elements != null) {
      for (var i = 0; i < elements.length; i++) {
        if (elements[i] == element) 
          return true;
      }
    }
    
    return false;
  },
  getSelectedElementAt: function(x, y){
    var elements = this.getSelectedElements();
    if (elements != null) {
      for (var i = 0; i < elements.length; i++) {
        var element = elements[i];
        var eBBox = fi.foyt.svg.svgdom.FNISVGDOMUtils.getScreenBBox(element);
        
        if ((eBBox.x <= x) && (eBBox.y <= y) && ((eBBox.x + eBBox.width) >= x) && ((eBBox.y + eBBox.height) >= y)) 
          return element;
      }
    }
    
    return null;
  },
  hasSelections: function(){
    return this._selectedElements.length > 0;
  },
  getSelectedElements: function(){
    return this._selectedElements;
  },
  clearElementSelection: function(){
    while (this._selectedElements.length > 0) {
      this._selectionDimensions = null;
      var element = this._selectedElements.pop();
      this._editorInstance.getSVGDocument().removeTemporaryElement(element._selectionRect);
      element._selectionRect = null;
    }
    
    this._updateIconset(null);
    this.fire("selectionChanged", {});
  },
  selectElementsWithin: function(sx1, sy1, sx2, sy2){
    if (this.getMode() != FNISelectionManager.MODE_NONE) {
      var elements = this._editorInstance.getSVGDocument().getElements();
      if (elements == null) 
        return;
      
      this._selectionDimensions = null;
      
      if (sx2 < sx1) {
        var t = sx2;
        sx2 = sx1;
        sx1 = t;
      }
      
      if (sy2 < sy1) {
        var t = sy2;
        sy2 = y1;
        sy1 = t;
      }
      
      while (this._selectedElements.length > 0) {
        this._selectionDimensions = null;
        var element = this._selectedElements.pop();
        this._editorInstance.getSVGDocument().removeTemporaryElement(element._selectionRect);
        element._selectionRect = null;
      }
      
      for (var i = 0; i < elements.length; i++) {
        var element = elements[i];
        var eBBox = fi.foyt.svg.svgdom.FNISVGDOMUtils.getScreenBBox(element);
        if (eBBox != null) {
          var ex1 = eBBox.x;
          var ey1 = eBBox.y;
          var ex2 = eBBox.x + eBBox.width;
          var ey2 = eBBox.y + eBBox.height;
          
          if (((ex1 > sx1) && (ex2 < sx2)) && ((sy1 < ey1) && (sy2 > ey2)) && (this._isGrouped(element) != true) && (this._elementInLayer(element, this._editorInstance.getSVGDocument().getCurrentLayer()))) 
            this._selectedElements.push(element);
        }
      }
      
      this.renderSelections();
      this.fire("selectionChanged", {});
    }
  },
  updateSelections: function(){
    this._selectionDimensions = null;
    this.renderSelections();    
  },
  renderSelections: function(){
    var selectionRect;
    for (var i = 0; i < this._selectedElements.length; i++) {
      var element = this._selectedElements[i];
      var elementBBox = fi.foyt.svg.svgdom.FNISVGDOMUtils.getScreenBBox(element);
      selectionRect = element._selectionRect;
      
      if (selectionRect == null) {
        selectionRect = this._editorInstance.getSVGDocument().createElement('rect');
        selectionRect.setAttribute('style', 'fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:1,6;');
        element._selectionRect = selectionRect;
        this._editorInstance.getSVGDocument().appendTemporaryElement(selectionRect);
      }
      
      this._rectangleController.moveTo(selectionRect, elementBBox.x, elementBBox.y);
      this._rectangleController.resizeTo(selectionRect, elementBBox.width, elementBBox.height);
    }
    
    this._updateIconset(this.getSelectionDimensions());
  },
  getSelectionDimensions: function(){
    if (!this._selectionDimensions) 
      this._selectionDimensions = fi.foyt.svg.svgdom.FNISVGDOMUtils.getElementSetScreenBBox(this.getSelectedElements());
    
    return this._selectionDimensions;
  },
  _onEditorMouseDown: function(event){
    if (this._activeCorner != null) {
      this.setMode(FNISelectionManager.MODE_DRAGGING_ICON);
      this.fire("iconDragStart", {
        dragStartX: event.dragStartX,
        dragStartY: event.dragStartY,
        corner: this._activeCorner
      });
    }
  },
  _onEditorMouseDragStart: function(event){
    switch (this.getMode()) {
      case FNISelectionManager.MODE_SELECT:
        this._moveSelectionBox(event.mouseEditorX, event.mouseEditorY, 0, 0);
        this.setMode(FNISelectionManager.MODE_SELECTING);
      break;
    }
  },
  _onEditorMouseDragEnd: function(event){
    switch (this.getMode()) {
      case FNISelectionManager.MODE_SELECTING:
        var rPos = this._selectionBox.getBBox();
        this.selectElementsWithin(rPos.x, rPos.y, rPos.x + rPos.width, rPos.y + rPos.height);
        this.setPreviousMode();
        this._hideSelectionBox();
      break;
      case FNISelectionManager.MODE_DRAGGING_ICON:
        this.setPreviousMode();
      break;
    }
  },
  _onEditorMouseDragging: function(event){
    switch (this.getMode()) {
      case FNISelectionManager.MODE_SELECTING:
        var width = event.mouseEditorX - event.dragStartX;
        var height = event.mouseEditorY - event.dragStartY;
        var x = event.dragStartX;
        var y = event.dragStartY;
        
        if (width < 0) {
          width = -width;
          x -= width;
        }
        
        if (height < 0) {
          height = -height;
          y -= height;
        }
        
        this._moveSelectionBox(x, y, width, height);
      break;
      case FNISelectionManager.MODE_DRAGGING_ICON:
        this.fire("iconDragging", {
          mouseEditorX: event.mouseEditorX,
          mouseEditorY: event.mouseEditorY,
          dragStartX: event.dragStartX,
          dragStartY: event.dragStartY,
          corner: this._activeCorner
        });
      break;
    }
  },
  _isGrouped: function(element){
    return this._getElementGroup(element) != null;
  }, 
  _getElementGroup: function (element) {
    var parentNode = element.parentNode; 
    while ((parentNode != null) && (parentNode.tagName.toUpperCase() != 'SVG')) {
      if ((parentNode.tagName.toUpperCase() == 'G') && (parentNode.getAttribute('fnigrouptype') != 'layer'))
        return parentNode;
      else
        parentNode = parentNode.parentNode; 
    }
    
    return null;
  },
  _elementInLayer: function (element, layer) {
	var parentNode = element.parentNode; 
	while ((parentNode != null) && (parentNode.tagName.toUpperCase() != 'SVG')) { 
	  if (parentNode.getAttribute('fnigrouptype') == 'layer') {
	    return parentNode == layer;
	  }
	  parentNode = parentNode.parentNode; 
	}
  },
  _loadIconset: function(iconsetName){
    if (this._iconsets == null) 
      this._iconsets = new Hash();
    
    var iconsetConf = this._editorInstance.getConfigurationManager().getValue('smIconsets')[iconsetName];
    
    var iconset = this._iconsets.get(iconsetName);
    
    if (iconset == undefined) {
      if (iconsetConf != undefined) {
        var iconset = new Array();
        iconset.push(fi.foyt.svg.svgdom.FNISVGDOMUtils.createElementFromJSON(this._editorInstance.getSVGDocument(), iconsetConf.TL.evalJSON()));
        iconset.push(fi.foyt.svg.svgdom.FNISVGDOMUtils.createElementFromJSON(this._editorInstance.getSVGDocument(), iconsetConf.TR.evalJSON()));
        iconset.push(fi.foyt.svg.svgdom.FNISVGDOMUtils.createElementFromJSON(this._editorInstance.getSVGDocument(), iconsetConf.BL.evalJSON()));
        iconset.push(fi.foyt.svg.svgdom.FNISVGDOMUtils.createElementFromJSON(this._editorInstance.getSVGDocument(), iconsetConf.BR.evalJSON()));
        this._iconsets.set(iconsetName, iconset);
      }
    }
    
    return {
      iconsetName: iconsetName,
//      iconset: iconset,
      style: iconsetConf.style.evalJSON(),
      activeStyle: iconsetConf.activeStyle.evalJSON(),
      width: iconsetConf.width,
      height: iconsetConf.height
    };
  },
  _updateIconset: function(bbox){
    if (this._currentIconsetConf != null) {
      if (bbox != null) {
        this._updateIconPos(FNISelectionManager.TOPLEFT_ICON, bbox.x, bbox.y);
        this._updateIconPos(FNISelectionManager.TOPRIGHT_ICON, bbox.x + bbox.width, bbox.y);
        this._updateIconPos(FNISelectionManager.BOTTOMLEFT_ICON, bbox.x, bbox.y + bbox.height);
        this._updateIconPos(FNISelectionManager.BOTTOMRIGHT_ICON, bbox.x + bbox.width, bbox.y + bbox.height);
      }
      else {
        var iconset = this._currentIconsetConf.iconset;
        
        FNISVGEditorElementStylingUtils.setStyles(iconset[FNISelectionManager.TOPLEFT_ICON], {
          display: 'none'
        });
        FNISVGEditorElementStylingUtils.setStyles(iconset[FNISelectionManager.TOPRIGHT_ICON], {
          display: 'none'
        });
        FNISVGEditorElementStylingUtils.setStyles(iconset[FNISelectionManager.BOTTOMLEFT_ICON], {
          display: 'none'
        });
        FNISVGEditorElementStylingUtils.setStyles(iconset[FNISelectionManager.BOTTOMRIGHT_ICON], {
          display: 'none'
        });
      }
    }
  },
  _updateIconPos: function(c, x, y){
    if (this._currentIconsetConf != null) {
      var element = this._currentIconsetConf.iconset[c];
      FNISVGEditorElementStylingUtils.setStyles(element, {
        display: 'block'
      });
      var elementBBox = fi.foyt.svg.svgdom.FNISVGDOMUtils.getScreenBBox(element);
      var controller = getSVGElementControllerVault().getElementControllerFor(element);
      controller.moveTo(element, x - (elementBBox.width / 2), y - (elementBBox.height / 2));
    }
  },
  _moveSelectionBox: function(x, y, width, height){
    if (!this._selectionBox) {
      this._selectionBox = this._editorInstance.getSVGDocument().createElement("rect");
      this._selectionBox.setAttribute('style', this._editorInstance.getConfigurationManager().getValue("selectionRectStyle"));
      this._editorInstance.getSVGDocument().appendTemporaryElement(this._selectionBox);
    }
    
    var controller = getSVGElementControllerVault().getElementControllerFor(this._selectionBox);
    controller.moveTo(this._selectionBox, x, y);
    controller.resizeTo(this._selectionBox, width, height);
  },
  _hideSelectionBox: function(){
    if (this._selectionBox != null) {
      this._editorInstance.getSVGDocument().removeTemporaryElement(this._selectionBox);
      this._selectionBox = null;
    }
  },
  _onDocumentMouseUp: function(event){
    switch (this.getMode()) {
      case FNISelectionManager.MODE_DRAGGING_ICON:
        this.setPreviousMode();
      break;
      case FNISelectionManager.MODE_MOUSE_OUT_WAITING:
        this.resetMode();
      break;
    }
  },
  _onEditorMouseOverElement: function (event) {
    switch (this.getMode()) {
      case FNISelectionManager.MODE_SELECT:
        var element = event.element;
        if (this.isSelected(element) != true)
          FNISVGEditorElementStylingUtils.setTemporaryStyle(element, this._getSelectableElementStyle());
        else {
          this.fire("mouseOverSelectedElement", {
            element: element
          });
        }         
      break;
    }      
  },
  _onEditorMouseOutElement: function (event) {
     switch (this.getMode()) {
      case FNISelectionManager.MODE_SELECT:
        var element = event.element;
        if (this.isSelected(element) != true)
          FNISVGEditorElementStylingUtils.removeTemporaryStyle(element);
        else {
          this.fire("mouseOutSelectedElement", {
            element: element
          });
        }  
      break;
    }    
  },
  _getSelectableElementStyle: function(){
    if (this._selectableElementStyle == undefined) {
      var confEntry = this._editorInstance.getConfigurationManager().getValue('selectableElementStyle');
      this._selectableElementStyle = confEntry;
    }
    
    return this._selectableElementStyle;
  },
  _onEditorMouseElementClick: function(event){
    var element = event.element;
    if (this.isSelected(element) != true) {
      if (event.ctrlKey != true)
        this.clearElementSelection();
      
      this.selectElement(element);
      FNISVGEditorElementStylingUtils.removeTemporaryStyle(element);  
    } else {
      this.fire("selectedElementClick", {
        element: element
      });
    }
  },
  _onEditorMouseMove: function (event) {    
    if (this._observeIcons == true) {
      var corner = null;
      
      var selectionDims = this.getSelectionDimensions();
      if (selectionDims != null) {
        var mx = event.mouseEditorX;
        var my = event.mouseEditorY;
        
        var hiw = this._iconWidth / 2;
        var hih = this._iconHeight / 2;
        
        var x1 = selectionDims.x;
        var y1 = selectionDims.y;
        var x2 = x1 + selectionDims.width;
        var y2 = y1 + selectionDims.height;
        
        var _t = (my > (y1 - hih)) && (my < ((y1 - hih) + this._iconHeight));
        var _l = (mx > (x1 - hiw)) && (mx < ((x1 - hiw) + this._iconWidth));
        var _r = (mx < (x2 + hiw)) && (mx > ((x2 + hiw) - this._iconWidth));
        var _b = (my < (y2 + hih)) && (my > ((y2 + hih) - this._iconHeight));
        
        if (_t == true) {
          if (_l == true) 
            corner = FNISelectionManager.TOPLEFT_ICON;
          else if (_r == true) 
            corner = FNISelectionManager.TOPRIGHT_ICON;
        }
        else if (_b == true) {
          if (_l == true) 
            corner = FNISelectionManager.BOTTOMLEFT_ICON;
          else if (_r == true) 
            corner = FNISelectionManager.BOTTOMRIGHT_ICON;
        }
      }
      
      this._changeIconStatus(corner);
    }
  },
  _changeIconStatus: function (corner) {
    if (this._activeCorner != null) {
      var icon = this._currentIconsetConf.iconset[this._activeCorner];
      FNISVGEditorElementStylingUtils.removeTemporaryStyle(icon);
      this.fire("mouseOutIcon", {
        icon: icon,
        corner: this._activeCorner
      });
    }
        
    if (corner != null) {
      var icon = this._currentIconsetConf.iconset[corner];      
      FNISVGEditorElementStylingUtils.setTemporaryStyle(icon, this._currentIconsetConf.activeStyle);
      this.fire("mouseOverIcon", {
        icon: icon,
        corner: corner
      });
    }
        
    this._activeCorner = corner;
  },
  _onMouseEnterEditorView: function (event) {
    if (this.getMode() == FNISelectionManager.MODE_MOUSE_OUT_WAITING) {
      this.setMode(FNISelectionManager.MODE_SELECT);
      this.setMode(FNISelectionManager.MODE_SELECTING);
    }
  },
  _onMouseExitEditorView: function (event) {
    if (this.getMode() == FNISelectionManager.MODE_SELECTING)
      this.setMode(FNISelectionManager.MODE_MOUSE_OUT_WAITING);
  }
});

Object.extend(FNISelectionManager.prototype, fni.events.FNIEventSupport);

Object.extend(FNISelectionManager, {
  /* modes */
  MODE_NONE: 'none',
  MODE_DRAGGING_ICON: 'draggingicon',
  MODE_SELECT: 'select',
  MODE_SELECTING: 'selecting',
  MODE_MOUSE_OUT_WAITING: 'waiting',
  /* icons */
  TOPLEFT_ICON: 0,
  TOPRIGHT_ICON: 1,
  BOTTOMLEFT_ICON: 2,
  BOTTOMRIGHT_ICON: 3
});